package com.bhudy.base;

import com.bhudy.annotation.ManyToOne;
import com.bhudy.annotation.OneToMany;
import com.bhudy.annotation.PrimaryKey;
import com.bhudy.annotation.SkipAttribute;
import com.bhudy.enums.Relation;
import com.bhudy.exception.SysException;
import com.bhudy.exception.SysExceptionCode;
import com.bhudy.utils.XqhUtils;
import org.apache.ibatis.jdbc.SQL;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;

class Mysql implements Sql {

    public String keywordLeft = "`";
    public String keywordRight = "`";

    @Override
    public String getKeyName(Class clazz) {
        Field[] fields = clazz.getDeclaredFields(); //获得私有的成员属性
        for (Field field : fields) {
            field.setAccessible(true);
            if (Modifier.isStatic(field.getModifiers())) continue;// 判断是否是静态属性
            if (field.isAnnotationPresent(PrimaryKey.class)) {
                return field.getName();
            }
        }
        return null;
    }

    @Override
    public String getLimitSql(String sql, QueryFilter queryFilter, List<Object> argList) {
        if (queryFilter.limits[0] != null && queryFilter.limits[1] != null) {
            argList.add(queryFilter.limits[0]);
            argList.add(queryFilter.limits[1]);
            sql += " LIMIT ? OFFSET ? ";
        }
        return sql;
    }

    @Override
    public <C> String updateSql(C entity, QueryFilter queryFilter, List<Object> argList) {
        SQL sql = new SQL().UPDATE(keywordLeft + XqhUtils.toUpperCaseFirstOneAdd(entity.getClass().getSimpleName()) + keywordRight);
        Field[] fields = entity.getClass().getDeclaredFields();

        for (Field field : fields) {
            field.setAccessible(true);
            if (Modifier.isStatic(field.getModifiers()) || field.isAnnotationPresent(ManyToOne.class) ||
                    field.isAnnotationPresent(OneToMany.class) || field.isAnnotationPresent(SkipAttribute.class))
                continue;// 判断是否是静态属性
            try {
                Object value = field.get(entity);
                if (value == null) continue;
                sql.SET(keywordLeft + XqhUtils.toUpperCaseFirstOneAdd(field.getName()) + keywordRight + " = ?");
                argList.add(value);
            } catch (IllegalAccessException e) {
                throw new SysException(e.getMessage());
            }
        }

        String sqlStr = sql.toString() + this.where(queryFilter, argList) + this.getOrderBy(queryFilter);
        return sqlStr;
    }

    @Override
    public <C> String updatesSql(List<C> entityList, List<Object> argList) {
        Class clazz = entityList.get(0).getClass();
        String keyName = this.getKeyName(clazz);
        if (keyName == null) throw new SysException(SysExceptionCode.CODE_40015);

        // Set<Object> pkSet = new HashSet<>();
        SQL sql = new SQL().UPDATE(keywordLeft + XqhUtils.toUpperCaseFirstOneAdd(clazz.getSimpleName()) + keywordRight);
        Map<String, String> columnMap = new HashMap<>();
        Field[] fields = clazz.getDeclaredFields();

        List<Object> keyValueList = new ArrayList<>();
        try {
            for (Field field : fields) {
                field.setAccessible(true);
                if (!field.isAnnotationPresent(PrimaryKey.class)) continue;// 判断是否是静态属性
                for (C entity : entityList) {
                    Object value = field.get(entity);
                    if (value == null) throw new SysException(SysExceptionCode.CODE_40028);
                    keyValueList.add(value);
                }
            }

            for (Field field : fields) {
                field.setAccessible(true);
                if (Modifier.isStatic(field.getModifiers()) || field.isAnnotationPresent(ManyToOne.class) || field.isAnnotationPresent(PrimaryKey.class) ||
                        field.isAnnotationPresent(OneToMany.class) || field.isAnnotationPresent(SkipAttribute.class))
                    continue;// 判断是否是静态属性
                for (int i = 0; i < entityList.size(); i++) {
                    C entity = entityList.get(i);
                    Object value = field.get(entity);
                    if (value == null) continue;

                    String column = Relation.WHEN.getValue() + "?" + Relation.THEN.getValue() + "?";
                    if (columnMap.containsKey(field.getName())) {
                        column = columnMap.get(field.getName()) + column;
                    }
                    columnMap.put(field.getName(), column);

                    argList.add(keyValueList.get(i));
                    argList.add(value);
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        if (columnMap.size() == 0) throw new SysException(SysExceptionCode.CODE_40003);
        StringBuffer columnSbSql = new StringBuffer(Relation.SET.getValue());
        for (Map.Entry<String, String> map : columnMap.entrySet()) {
            columnSbSql.append(keywordLeft + XqhUtils.toUpperCaseFirstOneAdd(map.getKey()) + keywordRight + " = " + Relation.CASE.getValue() + keywordLeft + XqhUtils.toUpperCaseFirstOneAdd(keyName) + keywordRight);
            columnSbSql.append(map.getValue() + Relation.END.getValue() + ", ");
        }

        QueryFilter queryFilter = new QueryFilter();
        if (keyValueList.size() == 1) {
            queryFilter.and(keyName, keyValueList.toArray()[0]);
        } else {
            queryFilter.and(keyName, Relation.IN, keyValueList.toArray());
        }

        String sqlStr = sql.toString() + columnSbSql.deleteCharAt(columnSbSql.length() - 2).toString() + this.where(queryFilter, argList) + this.getOrderBy(queryFilter);
        return sqlStr;
    }

    @Override
    public <C> String insertSql(List<C> entityList, List<Object> argList) {
        StringBuffer columnSqlSb = new StringBuffer();
        StringBuffer argsSqlSb = new StringBuffer();
        StringBuffer argSqlSb = new StringBuffer();

        for (int i = 0; i < entityList.size(); i++) {
            Class clazz = entityList.get(i).getClass();
            Field[] fields = clazz.getDeclaredFields();

            C entity = entityList.get(i);

            for (Field field : fields) {
                field.setAccessible(true);
                if (Modifier.isStatic(field.getModifiers())) continue;// 判断是否是静态属性
                if (!field.isAnnotationPresent(ManyToOne.class) && !field.isAnnotationPresent(OneToMany.class) && !field.isAnnotationPresent(SkipAttribute.class)) {
                    try {
                        Object value = field.get(entity);
                        if (field.isAnnotationPresent(PrimaryKey.class) && value == null) {
                            argList.add(XqhUtils.createId());
                        } else if (value != null) {
                            argList.add(value);
                        } else {
                            continue;
                        }

                        if (i == 0) {
                            columnSqlSb.append(keywordLeft + XqhUtils.toUpperCaseFirstOneAdd(field.getName()) + keywordRight + ",");
                            argSqlSb.append("?,");
                        }
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
            if (i == 0) {
                argSqlSb.deleteCharAt(argSqlSb.length() - 1);
                argSqlSb.insert(0, "(").append(")");
            }
            argsSqlSb.append(argSqlSb);
            if (entityList.size() - 1 != i) {
                argsSqlSb.append(",");
            }
        }
        columnSqlSb.deleteCharAt(columnSqlSb.length() - 1);
        columnSqlSb.insert(0, "(").append(")");

        String sql = new SQL().INSERT_INTO(keywordLeft + XqhUtils.toUpperCaseFirstOneAdd(entityList.get(0).getClass().getSimpleName()) + keywordRight).toString();
        sql += columnSqlSb.toString() + Relation.VALUES.getValue() + argsSqlSb.toString();
        return sql;
    }

    @Override
    public String deleteSql(Class clazz) {
        return new SQL().DELETE_FROM(keywordLeft + XqhUtils.toUpperCaseFirstOneAdd(clazz.getSimpleName()) + keywordRight).toString();
    }

    @Override
    public String countSql(Class clazz) {
        String entityName = keywordLeft + XqhUtils.toLowerCaseFirstOneDel(clazz.getSimpleName()) + keywordRight;
        Field[] fields = clazz.getDeclaredFields(); //获得私有的成员属性
        String key = null;
        for (Field field : fields) {
            field.setAccessible(true);
            if (Modifier.isStatic(field.getModifiers())) continue;// 判断是否是静态属性
            if (field.isAnnotationPresent(PrimaryKey.class)) {
                key = XqhUtils.toUpperCaseFirstOneAdd(field.getName());
                break;
            }
        }
        if (key == null) throw new SysException(SysExceptionCode.CODE_40015);

        return new SQL().SELECT("count(" + keywordLeft + key + keywordRight + ")")
                .FROM(keywordLeft + XqhUtils.toUpperCaseFirstOneAdd(clazz.getSimpleName()) + keywordRight + Relation.AS.getValue() + entityName).toString();
    }

    @Override
    public String selectSql(Class clazz) {
        String entityName = keywordLeft + XqhUtils.toLowerCaseFirstOneDel(clazz.getSimpleName()) + keywordRight;
        Field[] fields = clazz.getDeclaredFields(); //获得私有的成员属性
        StringBuffer columnsSb = new StringBuffer();
        Set<String> manyToOneTableSet = new HashSet<>();

        if (fields != null && fields.length > 0) {
            for (Field field : fields) {
                field.setAccessible(true);
                if (Modifier.isStatic(field.getModifiers())) continue;// 判断是否是静态属性
                if (!field.isAnnotationPresent(ManyToOne.class) && !field.isAnnotationPresent(OneToMany.class) && !field.isAnnotationPresent(SkipAttribute.class)) {
                    columnsSb.append(entityName + "." + keywordLeft + XqhUtils.toUpperCaseFirstOneAdd(field.getName()) + keywordRight + Relation.AS.getValue() + keywordLeft + field.getName() + keywordRight + ", ");
                }
            }
            columnsSb.deleteCharAt(columnsSb.length() - 2);
        }
        return new SQL().SELECT(columnsSb.toString())
                .FROM(keywordRight + XqhUtils.toUpperCaseFirstOneAdd(clazz.getSimpleName()) + keywordRight + Relation.AS.getValue() + entityName).toString();
    }

    @Override
    public String where(QueryFilter queryFilter, List<Object> argList) {
        StringBuffer whereSb = new StringBuffer();
        for (QueryFilter.WHERE where : queryFilter.whereList) {
            Relation relation1 = where.getRelation1();
            String name = where.getName();
            Relation relation2 = where.getRelation2();
            Object[] args = where.getArgs();
            String sql1 = where.getSql();
            if (where.getType() == 0) {
                String[] names = name.split("\\.");
                String nameFormat;
                if (name.length() == 0) {
                    nameFormat = null;
                } else if (names.length == 1) {
                    nameFormat = keywordLeft + XqhUtils.toUpperCaseFirstOneAdd(name) + keywordRight;
                } else {
                    nameFormat = keywordLeft + XqhUtils.toLowerCaseFirstOneDel(names[0]) + keywordRight + "." + keywordLeft + XqhUtils.toUpperCaseFirstOneAdd(names[1]) + keywordRight;
                }

                if (relation2 == Relation.IN) {
                    if (args == null || args.length == 0) {
                        throw new SysException(SysExceptionCode.CODE_40003);
                    }
                    StringBuffer whereArg = new StringBuffer();
                    for (Object arg : args) {
                        whereArg.append("?, ");
                        argList.add(arg);
                    }

                    if (whereArg.length() != 0) {
                        whereArg.deleteCharAt(whereArg.length() - 2);
                        whereArg.insert(0, "(");
                        whereArg.append(")");
                    }

                    whereSb.append(relation1.getValue() + nameFormat + relation2.getValue() + whereArg);
                } else if (relation2 == Relation.IS_NOT_NULL || relation2 == Relation.IS_NULL) {
                    whereSb.append(relation1.getValue() + nameFormat + relation2.getValue());
                } else if (relation2 == Relation.BETWEEN) {
                    if (args == null || args.length != 2) {
                        throw new SysException(SysExceptionCode.CODE_40003);
                    }
                    String str1 = "?" + Relation.AND.getValue() + "?";
                    argList.add(args[0]);
                    argList.add(args[1]);

                    whereSb.append(relation1.getValue() + nameFormat + relation2.getValue() + str1);
                } else {
                    if (args == null || args.length == 0) {
                        throw new SysException(SysExceptionCode.CODE_40003);
                    } else {
                        for (Object arg : args) {
                            argList.add(arg);
                            whereSb.append(relation1.getValue() + nameFormat + relation2.getValue() + "?");
                        }
                    }
                }
            } else {
                whereSb.append(sql1);
                if (XqhUtils.isBlank(args)) {
                    for (Object arg : args) {
                        argList.add(arg);
                    }
                }
            }
        }

        return whereSb.toString();
    }

    @Override
    public String getOrderBy(QueryFilter queryFilter) {
        StringBuffer sb = new StringBuffer();
        if (queryFilter.orderMap.size() != 0) {
            sb.append(" order by ");
            for (int i = 0; i < queryFilter.orderMap.size(); i++) {
                String[] strs = queryFilter.orderMap.get(i).split("_!");
                sb.append(keywordLeft + XqhUtils.toUpperCaseFirstOneAdd(strs[0]) + keywordRight + strs[1] + ",");
                /*whereSb.append(queryFilter.orderMap.get(i) + ",");*/
            }
            sb.deleteCharAt(sb.length() - 1);
        }

        return sb.toString();
    }

    @Override
    public String keyword(String name) {
        return keywordLeft + name + keywordRight;
    }
}